package br.ufal.tci.nexos.arcolive.beans;

import java.awt.Component;
import java.io.IOException;
import java.io.Serializable;

import javax.media.CannotRealizeException;
import javax.media.ControllerListener;
import javax.media.GainControl;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.NoDataSourceException;
import javax.media.NoPlayerException;
import javax.media.Player;
import javax.media.Time;
import javax.media.protocol.DataSource;

/**
 * ArCoLIVEPlayer.java
 * 
 * <p>
 * This class is a adequacy to Player Interface.
 * </p>
 * 
 * 
 * @author <a href="mailto:felipe@labpesquisas.tci.ufal.br">Felipe Barros Pontes</a>.
 * @author <a href="mailto:leandro@labpesquisas.tci.ufal.br">Leandro Melo de
 *         Sales</a>.
 * @since 0.1
 * @version 0.1
 * 
 * <p>
 * <b>Revisions:</b>
 * 
 * <p>
 * <b>20050317 leandro e felipe</b>
 * <ul>
 * <li> First version
 * </ul>
 */

public class ArCoLIVEPlayer implements Serializable {

	private DataSource dataSource = null;

	private Player player;

	private String playerType;

	private Time pauseTime, startTime, newPosition;

	private MediaLocator mediaLocator;

	private boolean repeat;

	/**
	 * 
	 */
	public ArCoLIVEPlayer() {
		this.playerType = "common";
	}

	/**
	 * @throws NoDataSourceException
	 * @throws NoPlayerException
	 * @throws IOException
	 */
	private void createPlayer() throws NoDataSourceException,
			NoPlayerException, IOException {
		if (this.dataSource == null) {
			this.dataSource = Manager.createDataSource(this.mediaLocator);
		}
		this.player = Manager.createPlayer(this.dataSource);
	}

	/**
	 * @throws NoDataSourceException
	 * @throws NoPlayerException
	 * @throws IOException
	 * @throws CannotRealizeException
	 */
	private void createRealizedPlayer() throws NoDataSourceException,
			NoPlayerException, IOException, CannotRealizeException {
		if (this.dataSource == null) {
			this.dataSource = Manager.createDataSource(this.mediaLocator);
		}
		this.player = Manager.createRealizedPlayer(this.dataSource);
	}

	/**
	 * @return
	 */
	public MediaLocator getMediaLocator() {
		return mediaLocator;
	}

	/**
	 * @param mediaLocator
	 */
	public void setMediaLocator(MediaLocator mediaLocator) {
		this.mediaLocator = mediaLocator;
	}

	/**
	 * @param playerType
	 */
	public void setPlayerType(String playerType) {
		this.playerType = playerType;
	}

	/**
	 * @return
	 */
	public String getPlayerType() {
		return this.playerType;
	}

	/**
	 * @return
	 */
	public Component getVisualComponent() {
		return this.player.getVisualComponent();
	}

	/**
	 * @return
	 */
	public Component getControlComponent() {
		return this.player.getControlPanelComponent();
	}

	/**
	 * @param listener
	 */
	public void addControllerListener(ControllerListener listener) {
		this.player.addControllerListener(listener);
	}

	/**
	 * @throws CannotPlayStreamException
	 */
	public void play() throws CannotPlayStreamException {
		String message = "Cannot play stream: ";
		try {
			if (this.player == null) {
				if (this.playerType.equals("common")) {
					this.createPlayer();
				} else {
					this.createRealizedPlayer();
				}
			}
		} catch (NoDataSourceException e) {
			throw new CannotPlayStreamException(message + e.getMessage());
		} catch (NoPlayerException e) {
			throw new CannotPlayStreamException(message + e.getMessage());
		} catch (IOException e) {
			throw new CannotPlayStreamException(message + e.getMessage());
		} catch (CannotRealizeException e) {
			throw new CannotPlayStreamException(message + e.getMessage());
		}
		if (this.player != null) {
			if (this.pauseTime != null) {
				this.player.setMediaTime(this.pauseTime);
			}
			if (this.startTime != null) {
				this.player.setMediaTime(this.startTime);
			}
			if (this.newPosition != null) {
				this.player.setMediaTime(this.newPosition);
				this.newPosition = null;
			}
			if (this.player.getTargetState() < Player.Started) {
				this.player.prefetch();
			}
			this.player.start();
		}
	}

	/**
	 * 
	 */
	public void pause() {
		if (this.player != null) {
			this.pauseTime = this.player.getMediaTime();
			this.player.stop();
		}
	}

	/**
	 * 
	 */
	public void stop() {
		this.player.stop();
	}

	/**
	 * 
	 */
	public void eject() {
		this.player.close();
		this.player.deallocate();
	}

	/**
	 * @param volume
	 */
	public void setVolume(int volume) {
		this.player.getGainControl().setLevel(this.intToFloat(volume));
	}

	/**
	 * @param rate
	 */
	public void incVolume(int rate) {
		GainControl gc = this.player.getGainControl();
		float level = gc.getLevel();
		float newLevel = level + this.intToFloat(rate);
		if (newLevel > 1) {
			newLevel = 1;
		}
		gc.setLevel(newLevel);
	}

	/**
	 * @param mute
	 */
	public void mute(boolean mute) {
		this.player.getGainControl().setMute(mute);
	}

	/**
	 * @param numInt
	 * @return
	 */
	private float intToFloat(int numInt) {
		int quantityDigits = getQuantityDigits(numInt);
		float divisor = 1;
		for (int i = 0; i < quantityDigits; i++) {
			divisor *= 10;
		}
		return numInt / divisor;
	}

	/**
	 * @param num
	 * @return
	 */
	private int getQuantityDigits(int num) {
		int quant = 0;
		do {
			num = num / 10;
			quant++;
		} while (num != 0);
		return quant;
	}

	/**
	 * @param repeat
	 */
	public void setRepetitive(boolean repeat) {
		this.repeat = repeat;
		if (repeat) {
			this.startTime = new Time(0.0);
		} else {
			this.startTime = null;
		}
	}

	/**
	 * @return
	 */
	public boolean isRepetitive() {
		return this.repeat;
	}

	/**
	 * @param rate
	 */
	public void setRate(float rate) {
		this.player.setRate(rate);
	}

	/**
	 * @param rate
	 * @throws CannotJumpToPositionException
	 */
	public void jumpToPosition(int rate) throws CannotJumpToPositionException {
		String message = "Cannot jump to position: ";
		Time currentPosition = player.getMediaTime();
		double seconds = currentPosition.getSeconds();
		this.newPosition = new Time(seconds + rate);
		this.stop();
		try {
			this.play();
		} catch (CannotPlayStreamException e) {
			throw new CannotJumpToPositionException(message + e.getMessage());
		}
	}

	/**
	 * @return
	 */
	public DataSource getDatasource() {
		return this.dataSource;
	}

	/**
	 * @param dataSource
	 */
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

}